<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Composer;
use Illuminate\Support\Str;

class MakeModelRepositoryControllerCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'make:cdmrv {name} {--mode=}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Make a repository model and controller and Migration';

    /**
     * @var
     */
    protected $name;

    /**
     * @var
     */
    protected $mode;

    /**
     * Create a new command instance.
     *
     * @param Filesystem $filesystem
     * @param Composer $composer
     */
    public function __construct(Filesystem $filesystem, Composer $composer)
    {
        parent::__construct();

        $this->files = $filesystem;
        $this->composer = $composer;
    }


    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //获取name 和 mode两个参数值
        $this->name = $this->argument('name');
        $this->mode = $this->option('mode');
        if (empty($this->mode)) {
            $this->mode = 'cdmrv';
        }
        if ($this->create()) {
            //若生成成功,则输出信息
            $this->info('Make Success');
        }
        //重新生成autoload.php文件
        $this->composer->dumpAutoloads();
    }

    private function create()
    {
        //渲染模板文件,替换模板文件中变量值
        $templates = $this->templateStub();
        $paths = $this->getPath();
        $class = null;
        foreach ($paths as $key => $path) {
            //根据不同路径,渲染对应的模板文件
            $this->line($path);
            $class = $this->files->put($path, $templates[$key]);
        }
        return $class;
    }


    private function getPath()
    {
        // 两个模板文件,对应的两个路径
        $mode = $this->mode;
        $paths = [];
        for ($i = 0; $i < strlen($mode); $i++) {
            $type = $mode[$i];
            switch ($type) {
                case 'm':
                    $directory = app_path('') . DIRECTORY_SEPARATOR . 'Models';
                    $name = $this->camelize($this->name) . '.php';
                    break;
                case 'c':
                    $directory = app_path('') . DIRECTORY_SEPARATOR . 'Http' . DIRECTORY_SEPARATOR . 'Controllers' . DIRECTORY_SEPARATOR . 'Admin';
                    $name = $this->camelize($this->name) . 'Controller.php';
                    break;
                case 'd':
                    $directory = database_path() . DIRECTORY_SEPARATOR . 'migrations';
                    $name = date('Y_m_d_His').'_create_' . $this->dbName() . '.php';
                    break;
                case 'v':
                    $directory = app_path() . DIRECTORY_SEPARATOR . 'Http' . DIRECTORY_SEPARATOR . 'Requests';
                    $name = $this->camelize($this->name) . 'SaveRequest.php';
                    break;
                default :
                    $directory = app_path('') . DIRECTORY_SEPARATOR . 'Repositories';
                    $name = $this->camelize($this->name) . 'Repository.php';
            }
            if (!$this->files->isDirectory($directory)) {
                return $this->files->makeDirectory($directory, 0755, true);
            }
            $paths[$type] = $directory . DIRECTORY_SEPARATOR . $name;
        }
        return $paths;
    }

    private function templateStub()
    {
        // 获取两个模板文件
        $stubs = $this->getStub();
        // 获取需要替换的模板文件中变量
        $templateData = $this->getTemplateData();
        $renderStubs = [];
        foreach ($stubs as $key => $stub) {
            // 进行模板渲染
            $renderStubs[$key] = $this->getRenderStub($templateData, $stub);
        }

        return $renderStubs;
    }

    private function getStub()
    {
        $dir = resource_path('stubs');
        $stubs = [
            'r' => $this->files->get($dir . DIRECTORY_SEPARATOR . 'repository.stub'),
            'm' => $this->files->get($dir . DIRECTORY_SEPARATOR . 'model.stub'),
            'c' => $this->files->get($dir . DIRECTORY_SEPARATOR . 'controller.stub'),
            'd' => $this->files->get($dir . DIRECTORY_SEPARATOR . 'migration.stub'),
            'v' => $this->files->get($dir . DIRECTORY_SEPARATOR . 'request.stub'),
        ];

        return $stubs;
    }

    private function getTemplateData()
    {
        $templateVar = [
            '__CamlName__' => $this->camelize($this->name),
            '__DBName__' => $this->dbName(),
            '__NamePluralStudly__' => Str::pluralStudly($this->camelize($this->name))
        ];

        return $templateVar;
    }

    private function getRenderStub($templateData, $stub)
    {
        foreach ($templateData as $search => $replace) {
            $stub = str_replace($search, $replace, $stub);
        }
        return $stub;
    }

    /**
     * 　　* 下划线转驼峰
     * 　　* 思路:
     * 　　* step1.原字符串转小写,原字符串中的分隔符用空格替换,在字符串开头加上分隔符
     * 　　* step2.将字符串中每个单词的首字母转换为大写,再去空格,去字符串首部附加的分隔符.
     * 　　*/
    function camelize($uncamelized_words, $separator = '_')
    {
        $uncamelized_words = $separator . str_replace($separator, " ", strtolower($uncamelized_words));
        return ucfirst(ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator));
    }

    /**
     * 　　* 驼峰命名转下划线命名
     * 　　* 思路:
     * 　　* 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写
     * 　　*/
    function uncamelize($camelCaps, $separator = '_')
    {
        return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
    }

    /**
     * 复数
     * @param $name
     * @return string|string[]|null
     */
    private function pluralize($name)
    {
        $rules = array(
            '/move$/i' => 'moves',
            '/foot$/i' => 'feet',
            '/child$/i' => 'children',
            '/human$/i' => 'humans',
            '/man$/i' => 'men',
            '/tooth$/i' => 'teeth',
            '/person$/i' => 'people',
            '/([m|l])ouse$/i' => '\1ice',
            '/(x|ch|ss|sh|us|as|is|os)$/i' => '\1es',
            '/([^aeiouy]|qu)y$/i' => '\1ies',
            '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
            '/(shea|lea|loa|thie)f$/i' => '\1ves',
            '/([ti])um$/i' => '\1a',
            '/(tomat|potat|ech|her|vet)o$/i' => '\1oes',
            '/(bu)s$/i' => '\1ses',
            '/(ax|test)is$/i' => '\1es',
            '/s$/' => 's',
        );
        foreach ($rules as $rule => $replacement) {
            if (preg_match($rule, $name))
                return preg_replace($rule, $replacement, $name);
        }
        return $name . 's';
    }

    private function dbName()
    {
        $underline = $this->uncamelize($this->name);
        $lins = explode('_', $underline);
        $last = count($lins) - 1;
        $lins[$last] = $this->pluralize($lins[$last]);
        return implode('_', $lins);
    }
}
